/**
 * Implementation of the methods fot the @ref clustering_instance
 * class
 * @author Carlos Colmenares (06-39380)
 * @author Kelwin Fernández (07-40888)
 */

#include "clustering_instance.h"

#define EPSILON 0.000000000001

using namespace std;


clustering_instance::clustering_instance(int nattrib, int ndata){

    n_data = ndata;
    n_attributes = nattrib;

    data = (double**)malloc(
            sizeof(double*)*ndata);

    for (int d=0; d < ndata; d++ ){
        data[d] = (double*)malloc(
                sizeof(double)*nattrib);
    }
    
    m = NULL;
    sd = NULL;
}

clustering_instance::clustering_instance(int nattrib, int ndata, double** da){
    
    n_data = ndata;
    n_attributes = nattrib;

    data = (double**)malloc(
            sizeof(double*)*ndata);

    for (int d=0; d < ndata; d++ ){
        data[d] = (double*)malloc(
                sizeof(double)*nattrib);
        for (int a=0; a < nattrib; a++){
            data[d][a] = da[d][a];
        }
    }
    
    m = NULL;
    sd = NULL;
}

clustering_instance::clustering_instance(string filename){
    ifstream fin(filename.c_str());

    n_data = 0;
    n_attributes = -1;

    string line;
    
    double next_attrib;
    queue<double*> tmp;
    double* next_point;

    while( fin >> line ){

        replace(line.begin(), line.end(), ',', ' ');

        stringstream ss;
        ss << line;

        if ( n_attributes == -1 ){
            n_attributes = 0;

            queue<double> tmp;
            while( ss >> next_attrib ){
                tmp.push(next_attrib);
                n_attributes++;
            }

            next_point = (double*)malloc(n_attributes*sizeof(double));

            for (int i=0; i<n_attributes; i++){
                next_point[i] = tmp.front();
                tmp.pop();
            }

        } else {

            next_point = (double*)malloc(n_attributes*sizeof(double));

            for (int i=0; i<n_attributes; i++){
                ss >> next_point[i];
            }

        }

        tmp.push( next_point );
        n_data++;
    }

    data = (double**)malloc(n_data*sizeof(double*));
    for (int i=0; i<n_data; i++){
        data[i] = tmp.front();
        tmp.pop();
    }
    
    m = NULL;
    sd = NULL;
}

int clustering_instance::get_num_attributes(){
    return n_attributes;
}

int clustering_instance::get_num_data(){
    return n_data;
}

double* clustering_instance::mean(){
    
    double* m = (double*)malloc(n_attributes*sizeof(double));
    
    for (int i=0; i<n_attributes; i++){
        m[i] = 0.0;
    }
    for (int i=0; i<n_data; i++){
        for (int j=0; j<n_attributes; j++){
            m[j] += data[i][j];
        }
    }
    for (int i=0; i<n_attributes; i++){
        m[i] /= n_data;
    }
    
    return m;
}

double* clustering_instance::standard_deviation(){
    double* m = mean();
    
    double* sd = (double*)malloc(n_attributes*sizeof(double));
    
    for (int i=0; i<n_attributes; i++){
        sd[i] = 0.0;
    }
    for (int i=0; i<n_data; i++){
        for (int j=0; j<n_attributes; j++){
            sd[j] += (data[i][j] - m[j])*(data[i][j] - m[j]);
        }
    }
    for (int i=0; i<n_attributes; i++){
        sd[i] = sqrt(sd[i]/(n_data-1));
    }
    
    free(m);
    return sd;
}

void clustering_instance::normalize(){
    if ( m == NULL ){
        m = (double*)malloc(n_attributes*sizeof(double));
        
        for (int a=0; a<n_attributes; a++){
            m[a] = data[0][a];
            for (int d=1; d<n_data; d++){
                m[a] = max(abs(data[d][a]),m[a]);
            }
            for (int d=0; d<n_data; d++){
                data[d][a] /= m[a];
            }
        }
    }
}

void clustering_instance::un_normalize(double* v){
    for (int i=0; i<n_attributes; i++){
        v[i] *= m[i];
    }
}
void clustering_instance::un_normalize(){
    for (int a=0; a<n_attributes; a++){
        for (int d=0; d<n_data; d++){
            data[d][a] *= m[a];
        }
    }
}

/*
void clustering_instance::normalize(){
    if ( m == NULL){
        m = mean();
    }
    if ( sd == NULL ){
        sd = standard_deviation();
    }
    
    for (int i=0; i<n_data; i++){
        for (int j=0; j<n_attributes; j++){
            data[i][j] = (data[i][j] - m[j])/sd[j];
        }
    }
}

void clustering_instance::un_normalize(double* v){
    for (int i=0; i<n_attributes; i++){
        v[i] = v[i]*sd[i] + m[i];
    }
}
*/
